from django.shortcuts import render, redirect
from django.views import View
from django import http
import re, json, logging
from users.models import User, Address
from django.db import DatabaseError
from django.urls import reverse
from django.contrib.auth import login, authenticate, logout
from meiduo_mall.utils.response_code import RETCODE
from django_redis import get_redis_connection
from django.contrib.auth.mixins import LoginRequiredMixin
from celery_tasks.email.tasks import send_verify_email
from users.utils import generate_verify_email_url
from users.utils import check_verify_email_token
from . import constants
from goods import models
from carts.utils import merge_cart_cookie_to_redis

#  创建日志输出器
logger = logging.getLogger('django')

# Create your views here.
class UserBrowseHistory(LoginRequiredMixin, View):
    """用户浏览记录"""
    def post(self, request):
        """保存用户浏览记录"""
        #  接收参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')

        #  校验参数
        try:
            models.SKU.objects.get(id=sku_id)
        except models.SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku不存在')

        #  保存用户浏览器
        redis_conn = get_redis_connection('history')
        p1 = redis_conn.pipeline()
        user_id = request.user.id

        #  先去重
        p1.lrem('history_%s'%user_id, 0, sku_id)
        #  再存储
        p1.lpush('history_%s'%user_id, sku_id)
        #  最后截取
        p1.ltrim('history_%s'%user_id ,0, 4)
        #  执行管道
        p1.execute()

        #  响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})

    def get(self, request):
        """获取用户浏览记录"""
        # 获取Redis存储的sku_id列表信息
        redis_conn = get_redis_connection('history')
        sku_ids = redis_conn.lrange('history_%s' % request.user.id, 0, -1)

        # 根据sku_ids列表数据，查询出商品sku信息
        skus = []
        for sku_id in sku_ids:
            sku = models.SKU.objects.get(id=sku_id)
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': skus})
    # def get(self,request):
    #     """获取用户浏览记录"""
    #     user = request.user
    #     #  获取redis存储的sku_id信息
    #     redis_conn = get_redis_connection('history')
    #     #  取出列表数据
    #     sku_ids = redis_conn.lrange('history_%s'%user.id, 0, -1)
    #
    #     #  查询sku信息
    #     skus = []
    #     for sku_id in sku_ids:
    #         sku = models.SKU.objects.get(id=sku_id)
    #         skus.append({
    #             'id':sku_id,
    #             'name': sku.name,
    #             'default_image_url': sku.default_image.url,
    #             'price': sku.price,
    #         })
    #     return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': skus})


class ChangePasswordView(LoginRequiredMixin, View):
    """修改密码"""
    def get(self, request):
        """展示修改密码界面"""
        return render(request, 'user_center_pass.html')

    def post(self, request):
        """实现修改密码逻辑"""
        #  接收参数
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        new_password2 = request.POST.get('new_password2')

        #  校验参数
        if not all([old_password, new_password, new_password2]):
            return http.HttpResponseForbidden('缺少必传参数')
        try:
            request.user.check_password(old_password)
        except Exception as e:
            logger.error(e)
            return render(request, 'user_center_pass.html', {'origin_pwd_errmsg': '原始密码错误'})
        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return http.HttpResponseForbidden('密码最少8位，最长20位')
        if new_password != new_password2:
            return http.HttpResponseForbidden('两次输入的密码不一致')

        #  修改密码
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            logger.error(e)
            return render(request, 'user_center_pass.html', {'change_pwd_errmsg': '修改密码失败'})

        #  清理状态保持信息
        logout(request)
        response = redirect(reverse('users:login'))
        response.delete_cookie('username')

        #  响应密码修改结果
        return response


class UpdateTitleAddressView(LoginRequiredMixin, View):
    """设置地址标题"""
    def put(self, request, address_id):
        """设置地址标题"""
        #  接收参数
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

        #  校验参数
        if not title:
            return http.HttpResponseForbidden('缺少title')

        try:
            #  查询地址
            address = Address.objects.get(id=address_id)

            #  设置新的地址标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置地址标题失败'})
        #  响应结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置地址标题成功'})


class DefaultAddressView(LoginRequiredMixin, View):
    """设置默认地址"""
    def put(self, request, address_id):
        """设置默认地址"""
        try:
            address = Address.objects.get(id=address_id)

            request.user.default_address = address
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})
        #  响应设置默认地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})


class UpadateDestroyAddressView(LoginRequiredMixin, View):
    """修改和删除地址"""
    def put(self, request, address_id):
        """修改地址"""
        #  接收参数
        json_str = request.body.decode()
        json_dir = json.loads(json_str)
        receiver = json_dir.get('receiver')
        province_id = json_dir.get('province_id')
        city_id = json_dir.get('city_id')
        district_id = json_dir.get('district_id')
        place = json_dir.get('place')
        mobile = json_dir.get('mobile')
        tel = json_dir.get('tel')
        email = json_dir.get('email')

        #  校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$)', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        #  判断地址是否存在，并更新地址信息
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '更新地址失败'})

        #  构造响应数据
        address = Address.objects.get(id=address_id)
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        #  响应更新地址结果
        return http.JsonResponse({'code':RETCODE.OK, 'errmsg': '更新地址成功', 'address': address_dict})

    def delete(self, request, address_id):
        """删除地址"""
        try:
            #  查询要删除的地址
            address = Address.objects.get(id=address_id)

            #  将地址逻辑删除设置为True
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})

        #  响应删除地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})


class CreateAddressView(LoginRequiredMixin, View):
    """新增地址"""
    def post(self, request):
        """实现新增地址逻辑"""
        #  判断地址数量是否超过上限
        count = request.user.addresses.count()
        if count > constants.VERIFY_EMAIL_TOKEN_EXPIRES:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址数量上限'})

        #  接收参数
        json_str = request.body.decode()
        json_dir = json.loads(json_str)
        receiver = json_dir.get('receiver')
        province_id = json_dir.get('province_id')
        city_id = json_dir.get('city_id')
        district_id = json_dir.get('district_id')
        place = json_dir.get('place')
        mobile = json_dir.get('mobile')
        tel = json_dir.get('tel')
        email = json_dir.get('email')

        #  校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$)', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        #  保存信息
        try:
            address = Address.objects.create(
                user = request.user,
                title = receiver,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email
            )
            #  设置默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})

        #  新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        #  响应保存结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict})


class AddressView(View):
    """用户收货地址"""
    def get(self, request):
        """提供收货地址界面"""
        #  获取当前登录用户
        login_user = request.user
        addresses = Address.objects.filter(user=login_user, is_deleted=False)

        address_dict_list = []
        for address in addresses:
            address_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            address_dict_list.append(address_dict)
        #  构造上下文
        context = {
            'default_address_id': login_user.default_address_id,
            'addresses': address_dict_list,
        }

        return render(request, 'user_center_site.html', context)

class VerifyEmailView(View):
    """验证邮箱"""
    def get(self, request):
        """实现邮箱验证逻辑"""
        #  接收参数
        token = request.GET.get('token')

        #  校验参数，判断token是否过期和为空
        if not token:
            return http.HttpResponseBadRequest('缺少token')

        user = check_verify_email_token(token)
        if not user:
            return http.HttpResponseForbidden('无效的token')

        #  修改email_active的值为True
        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return http.HttpResponseServerError('激活邮件失败')

        #  返回邮箱验证结果
        return redirect(reverse('users:info'))

class EmailView(View):
    """添加邮箱"""
    def put(self, request):
        """实现添加邮箱逻辑"""
        #  接收参数
        json_dict = json.loads(request.body.decode())
        email = json_dict.get('email')

        #  校验参数
        if not email:
            return http.HttpResponseForbidden('缺少email参数')
        if not re.match(r'[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.HttpResponseForbidden('参数email有误')

        # #  判断用户是否登录并返回JSON
        # if not request.user.is_authenticated():
        #     return http.JsonResponse({'code': RETCODE.SESSIONERR, 'errmsg': '用户未登录'})

        #  赋值email字段
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '添加邮箱失败'})

        #  异步发送验证邮件
        verify_url = generate_verify_email_url(request.user)
        send_verify_email.delay(email, verify_url)

        #  响应添加邮箱结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加邮箱成功'})


class UserInfoView(LoginRequiredMixin, View):
    '''用户中心'''
    def get(self, request):
        '''提供个人信息界面'''
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }
        return render(request, 'user_center_info.html', context)

class LogoutView(View):
    '''退出登录'''
    def get(self, request):
        '''实现退出登录逻辑'''
        #  清理session
        logout(request)
        #  退出登录，重定向到首页
        response = redirect(reverse('contents:index'))
        #  退出登录时要清除cookie中的username
        response.delete_cookie('username')

        return response

class LoginView(View):
    '''用户名登录'''
    def get(self, request):
        """
        提供登录界面
        :param request:请求对象
        :return: 登录界面
        """
        return render(request, 'login.html')

    def post(self, request):
        """
        实现登录逻辑
        :param request:请求对象
        :return: 登录结果
        """
        #  接收参数
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')

        #  校验参数
        #  判断参数是否齐全
        if not all([username, password]):
            return http.HttpResponseForbidden('缺少必传参数')

        #  判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入正确的用户名或手机号')

        #  判断密码是否是8-20个数字
        if not re.match(r'[a-zA-Z0-9]{8,20}$', password):
            return http.HttpResponseForbidden('密码最少8位，最长20位！')

        #  认证登录用户
        user = authenticate(username=username, password=password)
        if user is None:
            return render(request, 'login.html', {'account_errmsg': '用户名或密码错误'})

        #  实现状态保持
        login(request, user)
        #  设置状态保持的周期
        if remembered != 'on':
            #  没有记住用户
            request.session.set_expiry(0)
        else:
            #  记住用户：None默认为两周
            request.session.set_expiry(None)

        #  响应登录结果
        next = request.GET.get('next')
        if next:
            response = redirect(next)
        else:
            response = redirect(reverse('contents:index'))
        #  注册时用户写入到cookie，有效期是15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)

        #  合并购物车
        response = merge_cart_cookie_to_redis(request=request, user=user, response=response)

        #  响应登录结果:重定向到首页
        return response

class UsernameCountView(View):
    '''判断用户名是否重复注册'''
    def get(self, request,username):

        count = User.objects.filter(username=username).count()

        return http.JsonResponse({'code':RETCODE.OK, 'errmsg':'OK', 'count':count})

class MobileCountView(View):
    '''判断手机号是否重复注册'''
    def get(self, request,mobile):

        count = User.objects.filter(mobile=mobile).count()

        return http.JsonResponse({'code':RETCODE.OK, 'errmsg':'OK', 'count':count})

class RegisterView(View):
    '''用户注册'''

    def get(self, request):
        '''注册页面'''

        return render(request, 'register.html')

    def post(self, request):
        '''实现用户注册'''

        #  接收参数
        username = request.POST.get('username')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        mobile = request.POST.get('mobile')
        allow = request.POST.get('allow')
        #  接收短信验证码
        sms_code_client = request.POST.get('sms_code')


        #  校验参数
        #  判断参数是否齐全
        if not all([username, password, password2, mobile, allow]):
            return http.HttpResponseForbidden('缺少必传参数')

        # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入5-20个字符的用户名')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return http.HttpResponseForbidden('两次输入的密码不一致')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确的手机号码')
        # 判断是否勾选用户协议
        if allow != 'on':
            return http.HttpResponseForbidden('请勾选用户协议')
        #  保存注册数据之前，对比短信验证码
        redis_conn = get_redis_connection('verify_code')
        sms_code_server = redis_conn.get('sms_%s'% mobile)
        if sms_code_server is None:
            return render(request, 'register.html', {'sms_code_errmsg': '无效的短信验证码'})
        if sms_code_client != sms_code_server.decode():
            return render(request, 'register.html', {'sms_code_errmsg': '输入的短信验证码有误'})


        # return render(request, 'register.html', {'register_errmsg': '注册失败'})
        #  保存注册数据
        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except DatabaseError:
            return render(request, 'register.html', {'register_errmsg': '注册失败'})

        login(request, user)

        # return http.HttpResponse('注册成功，重定向到首页！')
        # return redirect(reverse('contents:index'))
        #  响应注册结果
        response = redirect(reverse('contents:index'))
        #  注册时用户写入到cookie，有效期是15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)

        #  响应注册结果:重定向到首页
        return response







